// the uranus dart SDK
import 'dart:typed_data';

import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'dart:convert';

import '../apis/api_constants.dart';

enum UranusMsgType {
  Text,
  Image,
  Voice,
  Video,
  Emotion,
  PersonCard,
  Like,
  AvailableCommand,
  File,
  Location,
}

class UranusChat {
  WebSocketChannel ws;

  String userToken;
  String userAddr;
  String deviceUUID;
  String deviceName;

  bool isOnline = false;

  UranusChat() {}

  void initConnection(userToken, userAddr, deviceUUID, deviceName) async {
    this.userToken = userToken;
    this.userAddr = userAddr;
    this.deviceUUID = deviceUUID;
    this.deviceName = deviceName;
    ws = new IOWebSocketChannel.connect(API.WS_URL);
    // globalWsStream = channel.stream.asBroadcastStream();
    var res = await _sayHi();
    if (res) {
      isOnline = true;
      ws.stream
          .listen(onReceiveMessage, onDone: onDisconnected, onError: onError);
    }
  }

  Future<bool> checkAlive() async {
    if (ws.closeCode != null) {
      return false;
    } else {
      return true;
    }
  }

  Future<bool> _sayHi() async {
    var hi =
        UranusChatSDK.getHiMsg(userToken, userAddr, deviceUUID, deviceName);
    ws.sink.add(hi);
    return true;
  }

  Future<bool> reConnect() async {
    var r = await Future.delayed(Duration(milliseconds: 1000)).then((_) {
      print('[uranus] reConnect re-try after 1s.');
      initConnection(userToken, userAddr, deviceUUID, deviceName);
      if (isOnline) {
        return true;
      } else {
        return false;
      }
    });
    return r;
  }

  void send(String msgStr) {
    ws.sink.add(msgStr);
  }

  void onReceiveMessage(event) {
    throw UnimplementedError();
  }

  void onDisconnected() {
    throw UnimplementedError();
  }

  void onError(error) {
    throw UnimplementedError();
  }
}

class UranusChatSDK {
  /////////////////////// v2 API ///////////////////////

  static getTxtMsgV2(
      String content, String sender, String target, String senderName) {
    var msg = {
      "send_type": 0,
      "target": target,
      "sender": sender,
      "sender_name": senderName,
      "content": content,
      "msg_type": UranusMsgType.Text.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getFunctionalMsgCommands(
      String content, String sender, String target, String senderName) {
    var msg = {
      "send_type": 0,
      "target": target,
      "sender": sender,
      "sender_name": senderName,
      "content": content,
      "msg_type": UranusMsgType.AvailableCommand.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  /////////////////////// v2 END ///////////////////////
  static getTxtMsg(String content, String sender, String target,
      String senderName, String targetName, String sessionUID) {
    // every message should have a sessionUID
    var msg = {
      "send_type": 0,
      "target": target,
      "target_name": targetName,
      "sender": sender,
      "sender_name": senderName,
      "content": content,
      "msg_type": UranusMsgType.Text.index,
      "time": DateTime.now().toIso8601String(),
      "session_uid": sessionUID
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getPersonCard(String content, String sender, String target,
      String senderName, String targetName) {
    var msg = {
      "target": target,
      "target_name": targetName,
      "sender": sender,
      "sender_name": senderName,
      "content": content,
      "msg_type": UranusMsgType.PersonCard.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getEmotionMsg(String emotionID, String sender, String target,
      String senderName, String targetName) {
    var msg = {
      "target": target,
      "target_name": targetName,
      "sender": sender,
      "sender_name": senderName,
      "content": emotionID,
      "msg_type": UranusMsgType.Emotion.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getLikeMsg(
      String sender, String target, String senderName, String targetName) {
    var msg = {
      "target": target,
      "target_name": targetName,
      "sender": sender,
      "sender_name": senderName,
      "content": "like",
      "msg_type": UranusMsgType.Like.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  // getImageMsg send image bytes directly, and repost via server
  static getImageMsg(Uint8List imgBytes, String sender, String target,
      String senderName, String targetName) {
    var msg = {
      "target": target,
      "target_name": targetName,
      "sender": sender,
      "sender_name": senderName,
      "content": '',
      'content_bytes': imgBytes,
      "msg_type": UranusMsgType.Image.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getImageMsgGroup(Uint8List imgBytes, String sender, String target,
      String senderName, String targetName) {
    var msg = {
      "send_type": 1,
      "target": target,
      "target_name": targetName,
      "sender": sender,
      "sender_name": senderName,
      "content": '',
      'content_bytes': imgBytes,
      "msg_type": UranusMsgType.Image.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  // getImageMsgV2
  static getImageMsgV2(Uint8List imgBytes, String imgUrl, String sender,
      String target, String senderName, String targetName) {
    var msg = {
      "target": target,
      "target_name": targetName,
      "sender": sender,
      "sender_name": senderName,
      // default value is "imgUrl", server will try decode content is url or not
      // if it is url, then directly using it, otherwise converts bytes to url
      "content": imgUrl,
      'content_bytes': imgBytes,
      "msg_type": UranusMsgType.Image.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  ///////////////////////// Group methods ////////////////////////////////
  static getTxtMsgGroup(String content, String sender, String senderName,
      String groupAddr, String groupName) {
    var msg = {
      "send_type": 1,
      "group_addr": groupAddr,
      "group_name": groupName,
      "sender": sender,
      "sender_name": senderName,
      "content": content,
      "msg_type": UranusMsgType.Text.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getEmotionMsgGroup(String emotionID, String sender, String senderName,
      String groupAddr, String groupName) {
    var msg = {
      "send_type": 1,
      "group_addr": groupAddr,
      "group_name": groupName,
      "sender": sender,
      "sender_name": senderName,
      "content": emotionID,
      "msg_type": UranusMsgType.Emotion.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getImageMsgV2Group(Uint8List imgBytes, String imgUrl, String sender,
      String senderName, String groupAddr, String groupName) {
    var msg = {
      "send_type": 1,
      "group_addr": groupAddr,
      "group_name": groupName,
      "sender": sender,
      "sender_name": senderName,
      "content": imgUrl,
      'content_bytes': imgBytes,
      "msg_type": UranusMsgType.Image.index,
      "time": DateTime.now().toIso8601String(),
    };
    var outMsg = {"purpose": "send", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getHiMsg(
      String token, String sender, String deviceUUID, String deviceName) {
    var msg = {
      "token": token,
      "user_addr": sender,
      "ua": "dart/fluter-v0.0.1",
      "device": "Phone",
      "location": "湖南长沙",
      "device_name": deviceName,
      "device_uuid": deviceUUID
    };
    var outMsg = {"purpose": "hi", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }

  static getEchoReadMsg(String sessionId, int serverId) {
    var msg = {
      "session_id": sessionId,
      "server_id": serverId,
    };
    var outMsg = {"purpose": "echo/read", "payload": msg};
    var outStr = json.encode(outMsg);
    return outStr;
  }
}
